package com.shbatis.core;

import com.shbatis.datasource.pooled.PooledDataSource;
import com.shbatis.datasource.unpooled.UnPooledDataSourceFactory;
import com.shbatis.transaction.Transaction;
import com.shbatis.transaction.jdbc.JdbcTransaction;
import com.shbatis.utils.Resources;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Auther: sh
 * @Version 1.0
 * @Description: 连接工厂构建器
 */
public class SqlSessionFactoryBuilder {


    public SqlSessionFactoryBuilder() {
    }

    public SqlSessionFactory build(InputStream in) {

        try {
            Document document = new SAXReader().read(in);

            Element environmentsElt = (Element) document.selectSingleNode("/configuration/environments");
            String defaultEnv = environmentsElt.attributeValue("default");
            Element environmentElt = (Element) document.selectSingleNode("/configuration/environments/environment[@id='" + defaultEnv + "']");
            // 解析配置文件，创建数据源对象
            Element dataSourceElt = environmentElt.element("dataSource");
            DataSource dataSource = getDataSource(dataSourceElt);
            // 解析配置文件，创建事务管理器对象
            Element transactionManagerElt = environmentElt.element("transactionManager");
            Transaction transactionManager = getTransactionManager(transactionManagerElt, dataSource);
            // 解析配置文件，获取所有的SQL映射对象
            Element mappers = environmentsElt.element("mappers");
            Map<String, ShMappedStatement> mappedStatements = getMappedStatements(mappers);

            // 返回
            return new SqlSessionFactory(transactionManager, mappedStatements);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, ShMappedStatement> getMappedStatements(Element mappers) {
        Map<String, ShMappedStatement> mappedStatements = new HashMap<>();
        mappers.elements().forEach(mapperElt -> {
            try {
                String resource = mapperElt.attributeValue("resource");
                SAXReader saxReader = new SAXReader();
                Document document = saxReader.read(Resources.getResourcesAsStream(resource));
                Element mapper = (Element) document.selectSingleNode("/mapper");
                String namespace = mapper.attributeValue("namespace");

                mapper.elements().forEach(sqlMapper->{
                    String sqlId = sqlMapper.attributeValue("id");
                    String sql = sqlMapper.getTextTrim();
                    String parameterType = sqlMapper.attributeValue("parameterType");
                    String resultType = sqlMapper.attributeValue("resultType");
                    String sqlType = sqlMapper.getName().toLowerCase();
                    ShMappedStatement shMappedStatement = new ShMappedStatement(sqlId, resultType, sql, parameterType, sqlType);
                    mappedStatements.put(namespace + "." + sqlId, shMappedStatement);
                });
            } catch (Exception e) {
                throw new RuntimeException("加载mapper文件失败", e);
            }
        });
        return mappedStatements;
    }

    private Transaction getTransactionManager(Element transactionManagerElt, DataSource dataSource) {
        String type = transactionManagerElt.attributeValue("type");
        Transaction transactionManager = null;
        if ("JDBC".equals(type)) {
            transactionManager = new JdbcTransaction(dataSource, false);
        } else if ("MANAGED".equals(type)) {
            transactionManager = new JdbcTransaction(dataSource, false);
        }
        return transactionManager;
    }

    private DataSource getDataSource(Element dataSourceElt) {

        Map<String, String> dataSourceMap = new HashMap<>();
        List<Element> property = dataSourceElt.elements();
        property.forEach(propertyELt -> {
            String name = propertyELt.attributeValue("name");
            String value = propertyELt.attributeValue("value");
            dataSourceMap.put(name, value);
        });

        String dataSourceType = dataSourceElt.attributeValue("type").toUpperCase();
        DataSource dataSource = null;
        if ("POOLED".equals(dataSourceType)) {
            dataSource = new PooledDataSource();
        } else if ("UNPOOLED".equals(dataSourceType)) {
            dataSource = UnPooledDataSourceFactory.getDataSource(dataSourceMap);
        } else if ("JNDI".equals(dataSourceType)) {
            return null;
        }
        return dataSource;
    }
}

